package database

import (
	"context"
	"github.com/redis/go-redis/v9"
	"github.com/star-find-cloud/star-chat/conf"
	log "github.com/star-find-cloud/star-chat/pkg/logger"
	"os"
	"sync"
	"time"
)

type Redis struct {
	//rdb *redis.ClusterClient
	cache *redis.Client
}

var (
	_redis  = &Redis{}
	rdbOnce sync.Once
)

// 生产环境取消以下注释, 改用集群模式
//func initRedis() (*redis.Client, error) {
//	var (
//		rdb *redis.Client
//		err error
//	)
//
//	rdbOnce.Do(func() {
//		var (
//			c = conf.GetConfig()
//			//addrs []string
//		)
//		masterAddr := c.Database.Redis.MasterHost + ":" + c.Database.Redis.MasterPort
//		//for _, slave := range c.Database.Redis.Slaves {
//		//	slaveAddr := slave.Host + ":" + slave.Port
//		//	addrs = append(addrs, slaveAddr)
//		//}
//		//addrs = append(addrs, masterAddr)
//
//		//rdb := redis.NewClusterClient(&redis.ClusterOptions{
//		//	Addrs:    addrs,
//		//	Password: c.Database.Redis.Password,
//		//	PoolSize: c.Database.Redis.PoolSize,
//		//	// 将读命令发送到从节点
//		//	ReadOnly: true,
//		//	// 优先选择延迟最低的节点
//		//	RouteByLatency: true,
//		//	// 连接超时时间
//		//	DialTimeout:    c.Database.Redis.DialTimeout * time.Second,
//		//	WriteTimeout:   c.Database.Redis.WriteTimeout * time.Second,
//		//	ReadTimeout:    c.Database.Redis.ReadTimeout * time.Second,
//		//	MaxIdleConns:   c.Database.Redis.MaxIdleConns,
//		//	MaxActiveConns: c.Database.Redis.MaxActiveConns,
//		//})
//
//		rdb = redis.NewClient(&redis.Options{
//			Addr:         masterAddr,
//			Password:     c.Database.Redis.Password,
//			PoolSize:     c.Database.Redis.PoolSize,
//			DB:           c.Database.Redis.Database,
//			DialTimeout:  c.Database.Redis.DialTimeout * time.Second,
//			ReadTimeout:  c.Database.Redis.ReadTimeout * time.Second,
//			WriteTimeout: c.Database.Redis.WriteTimeout * time.Second,
//		})
//
//		err = rdb.Ping(context.Background()).Err()
//	})
//	return rdb, err
//}

func initRedis() (*redis.Client, error) {
	var (
		rdb *redis.Client
		err error
	)

	rdbOnce.Do(func() {
		var (
			c = conf.GetConfig()
			//addrs []string
		)
		masterAddr := c.Database.Redis.MasterHost + ":" + c.Database.Redis.MasterPort
		//for _, slave := range c.Database.Redis.Slaves {
		//	slaveAddr := slave.Host + ":" + slave.Port
		//	addrs = append(addrs, slaveAddr)
		//}
		//addrs = append(addrs, masterAddr)

		//rdb := redis.NewClusterClient(&redis.ClusterOptions{
		//	Addrs:    addrs,
		//	Password: c.Database.Redis.Password,
		//	PoolSize: c.Database.Redis.PoolSize,
		//	// 将读命令发送到从节点
		//	ReadOnly: true,
		//	// 优先选择延迟最低的节点
		//	RouteByLatency: true,
		//	// 连接超时时间
		//	DialTimeout:    c.Database.Redis.DialTimeout * time.Second,
		//	WriteTimeout:   c.Database.Redis.WriteTimeout * time.Second,
		//	ReadTimeout:    c.Database.Redis.ReadTimeout * time.Second,
		//	MaxIdleConns:   c.Database.Redis.MaxIdleConns,
		//	MaxActiveConns: c.Database.Redis.MaxActiveConns,
		//})

		rdb = redis.NewClient(&redis.Options{
			Addr:         masterAddr,
			Password:     c.Database.Redis.Password,
			PoolSize:     c.Database.Redis.PoolSize,
			DB:           c.Database.Redis.Database,
			DialTimeout:  c.Database.Redis.DialTimeout * time.Second,
			ReadTimeout:  c.Database.Redis.ReadTimeout * time.Second,
			WriteTimeout: c.Database.Redis.WriteTimeout * time.Second,
		})

		err = rdb.Ping(context.Background()).Err()
	})
	return rdb, err
}

func NewRedis() (*Redis, error) {
	cache, err := initRedis()
	_redis.cache = cache
	if err != nil {
		log.CacheLogger.Errorf("redis connect faild: %v", err)
		os.Exit(1)
	} else {
		log.CacheLogger.Infoln("redis connect success")
	}
	return _redis, nil
}

func (r *Redis) GetCache() *redis.Client {
	return _redis.cache
}

// GetLock 获取分布式锁
func (r *Redis) GetLock(ctx context.Context, lockKey, lockValue string) (bool, error) {
	result, err := r.cache.SetNX(ctx, lockKey, lockValue, 10*time.Second).Result()
	if err != nil {
		return false, err
	}
	return result, nil
}

// ReleaseLock 释放分布式锁
func (r *Redis) ReleaseLock(ctx context.Context, lockKey, lockValue string) {
	currentValue, err := r.cache.Get(ctx, lockKey).Result()
	if err != nil {
		log.CacheLogger.Errorf("get lock value failed: %v", err)
		return
	}

	if currentValue == lockValue {
		for i := 0; i < 5; i++ {
			err = r.cache.Del(ctx, lockKey).Err()
			if err == nil {
				break
			} else {
				log.CacheLogger.Errorf("delete lock failed: %v", err)
				return
			}
		}
	}
	return
}
